{% extends "tutorial.html" %}

{% block headauthor %}Eric Bidelman <e.bidelman@chromium.org>{% endblock %}

{% block headtitle %}Conceitos básicos sobre Web Workers{% endblock %}
{% block pagetitle %}Conceitos básicos sobre Web Workers{% endblock %}
{% block pagebreadcrumb %}Conceitos básicos sobre Web Workers{% endblock %}
{% block head %}
<style>
.example {
  padding: 10px;
  border: 1px solid #ccc;
}
</style>
{% endblock %}
{% block date %}26 de julho de 2010{% endblock %}
{% block updated %}24 de maio de 2011{% endblock %}

{% block browsersupport %}
<span class="browser opera supported"><span class="browser_name">Opera</span><span class="support">compatível</span></span> <span class="browser ie"><span class="browser_name">Internet Explorer</span><span class="support">não compatível</span></span> <span class="browser safari supported"><span class="browser_name">Safari</span><span class="support">compatível</span></span> <span class="browser ff supported"><span class="browser_name">Firefox</span><span class="support">compatível</span></span> <span class="browser chrome supported"><span class="browser_name">Google Chrome</span><span class="support">compatível</span></span>
{% endblock %}

{% block html5badge %}
<img src="/static/images/identity/html5-badge-h-performance.png" width="133" height="64" alt="Este artigo tem tecnologia de desempenho e integração HTML5" title="Este artigo tem tecnologia de desempenho e integração HTML5"  />
{% endblock %}

{% block iscompatible %}
  return Modernizr.webworkers;
{% endblock %}

{% block content %}
  <h2 id="toc-introduction-jsconcurrency">O problema: simultaneidade do JavaScript</h2>
  <p>Existem vários gargalos que impedem a portabilidade de aplicativos interessantes (por exemplo, de implementações pesadas de servidor) para o JavaScript no lado do cliente. Entre eles, estão a compatibilidade de navegador, os tipos estáticos, a acessibilidade e o desempenho. Felizmente, este último problema vem sendo superado rapidamente, à medida que os fornecedores de navegadores aprimoram a velocidade dos seus mecanismos de JavaScript.</p>

  <p>Na verdade, uma questão que continua sendo um obstáculo para o JavaScript é a própria linguagem. O JavaScript é um ambiente de sequência única, isso significa que não é possível executar vários scripts ao mesmo tempo. Por exemplo, imagine um site que precisa manipular eventos de interface do usuário, consultar e processar grandes quantidades de dados de API e manipular o DOM. Bem comum, não é mesmo? Infelizmente, nem tudo isso pode ser feito ao mesmo tempo, devido às limitações no tempo de execução do JavaScript dos navegadores. A execução do script ocorre dentro de uma única sequência.</p>

  <p>Os desenvolvedores emulam a "simultaneidade" usando técnicas como <code>setTimeout()</code>, <code>setInterval()</code>, <code>XMLHttpRequest</code> e até mesmo manipuladores de eventos. Sim, todos esses recursos são executados de forma assíncrona, o modelo sem bloqueio não significa necessariamente simultaneidade. Eventos assíncronos são processados depois que o script de execução atual gera seus resultados. A boa notícia é que o HTML5 oferece muito mais do que isso.</p>

  <h2 id="toc-introduction-jsthreading">Introdução ao Web Workers: como trazer o sequenciamento ao JavaScript.</h2>

  <p>A especificação <a href="http://www.whatwg.org/specs/web-workers/current-work/">Web Workers</a> (link em inglês) define uma API para geração de scripts de segundo plano no seu aplicativo da web. O Web Workers permite executar tarefas como disparar scripts de longa duração para executar tarefas muito dispendiosas, mas sem bloquear a interface de usuário ou outros scripts para manipular interações com o usuário. Isso poderá ser o fim daquele diálogo de "script não responde" que todos nós adoramos:</p>

  <figure class="center">
    <img src="/static/images/screenshots/workers/unresponsive_script.gif" width="450" height="100" title="Diálogo "script não response"" alt="Diálogo "script não responde""  />
    <figcaption>Diálogo "script não responde" comum.</figcaption>
  </figure>

  <p>O Workers utiliza a transmissão de mensagem do tipo sequência para obter paralelismo. É perfeito para manter a interface atualizada, com desempenho e responsiva para os usuários.</p>

  <h3 id="toc-introduction-types">Tipos de Web Workers</h3>

  <p>É interessante observar que a <a href="http://www.whatwg.org/specs/web-workers/current-work/">especificação</a> (link em inglês) discute  dois tipos de Web Workers, <a href="http://www.whatwg.org/specs/web-workers/current-work/#dedicated-workers-and-the-worker-interface">Workers dedicados</a> (link em inglês) e <a href="http://www.whatwg.org/specs/web-workers/current-work/#sharedworker">Workers compartilhados</a> (link em inglês). Este artigo trata somente dos workers dedicados e refere-se a eles como "web workers" ou "workers".</p>

  <h2 id="toc-gettingstarted">Primeiros passos</h2>

  <p>Os Web Workers são executados em uma sequência isolada. Como resultado, o código que eles executam precisa estar contido em um arquivo separado. Mas, antes de fazer isso, a primeira coisa a fazer é criar um novo objeto <code>Worker</code> na página principal. O construtor leva o nome do script do worker:</p>

  <pre class="prettyprint">
var worker = new Worker('task.js');
</pre>

  <p>Se o arquivo especificado existir, o navegador gerará uma nova sequência do worker, que será transferida por download assíncrono. O worker só é iniciado quando o arquivo tiver sido transferido completamente e executado. Se o caminho para o worker retornar um 404, o worker tem falha silenciosa.</p>

  <p>Depois de criar o worker, inicie-o chamando o método <code>postMessage()</code>:</p>
  <pre class="prettyprint">
worker.postMessage(); // Start the worker.
</pre>

  <h3 id="toc-gettingstarted-workercomm">Comunicação com Workers por transmissão de mensagem</h3>

  <p>A comunicação entre um worker e sua página de origem é feita por um modelo de evento e pelo método <code>postMessage()</code>. Dependendo do navegador/versão, o <code>postMessage()</code> pode aceitar uma string de texto ou um objeto JSON como seu único argumento. As versões mais recentes dos navegadores modernos suportam a transmissão de um objeto JSON.</p>

  <p>Abaixo, temos um exemplo do uso de uma string para transmitir "Hello World" ("Olá, Mundo") para um worker em doWork.js. O worker simplesmente retorna a mensagem que foi transmitida para ele.</p>

  <p>Script principal:</p>

  <pre class="prettyprint">
var worker = new Worker('doWork.js');

worker.addEventListener('message', function(e) {
  console.log('Worker said: ', e.data);
}, false);

worker.postMessage('Hello World'); // Send data to our worker.
</pre>

  <p>doWork.js (o worker):</p>

  <pre class="prettyprint">
self.addEventListener('message', function(e) {
  self.postMessage(e.data);
}, false);
</pre>

  <p>Quando <code>postMessage()</code> é chamado da página principal, nosso worker manipula a mensagem definindo um manipulador <code>onmessage</code> para o evento <code>message</code>. A carga útil da mensagem (no caso "Hello World") fica acessível em <code>Event.data</code>. Ainda que este exemplo em especial não seja muito emocionante, ele demonstra que <code>postMessage()</code> também é um meio de transmitir dados de volta para a sequência principal. Prático!</p>

  <p>As mensagens transmitidas entre a página principal e os workers são copiadas, não compartilhadas. Por exemplo, no próximo exemplo a propriedade "msg" da mensagem JSON está acessível em ambos os locais. Parece que o objeto está sendo transmitido diretamente para o worker ainda que esteja em execução em um espaço separado, específico. Na verdade, o que acontece é que o objeto está sendo serializado enquanto é entregue para o worker e, subsequentemente, desserializado na outra terminação. A página e o worker não compartilham a mesma instância, portanto o resultado final é uma duplicata criada em cada transmissão. A maioria dos navegadores implementa este recurso pela codificação / decodificação JSON automática do valor em uma das terminações.</p>

  <p>O exemplo a seguir é mais complexo, as mensagens são transmitidas usando os objetos JSON.</p>

  <p>Script principal:</p>

  <pre class="prettyprint">
&lt;button onclick="sayHI()"&gt;Say HI&lt;/button&gt;
&lt;button onclick="unknownCmd()"&gt;Send unknown command&lt;/button&gt;
&lt;button onclick="stop()"&gt;Stop worker&lt;/button&gt;
&lt;output id="result"&gt;&lt;/output&gt;

&lt;script&gt;
  function sayHI() {
    worker.postMessage({'cmd': 'start', 'msg': 'Hi'});
  }

  function stop() {
    // Calling worker.terminate() from this script would also stop the worker.
    worker.postMessage({'cmd': 'stop', 'msg': 'Bye'});
  }

  function unknownCmd() {
    worker.postMessage({'cmd': 'foobard', 'msg': '???'});
  }

  var worker = new Worker('doWork2.js');

  worker.addEventListener('message', function(e) {
    document.getElementById('result').textContent = e.data;
  }, false);
&lt;/script&gt;
</pre>

  <p>doWork2.js:</p>

  <pre class="prettyprint">
self.addEventListener('message', function(e) {
  var data = e.data;
  switch (data.cmd) {
    case 'start':
      self.postMessage('WORKER STARTED: ' + data.msg);
      break;
    case 'stop':
      self.postMessage('WORKER STOPPED: ' + data.msg + '. (buttons will no longer work)');
      self.close(); // Terminates the worker.
      break;
    default:
      self.postMessage('Unknown command: ' + data.msg);
  };
}, false);
</pre>

  <p><strong>Observação</strong>: existem duas maneiras de parar um worker: chamar <code>worker.terminate()</code> na página principal ou chamar <code>self.close()</code> dentro do próprio worker.</p>

  <p><strong>Exemplo</strong>: execute este worker!</p>
  <div class="example">
    <button onclick="sayHI()">Dizer OI</button>
    <button onclick="unknownCmd()">Enviar comando desconhecido</button>
    <button onclick="stop()">Parar worker</button>
    <output id="result"></output>
  </div>

  <h2 id="toc-enviornment">O ambiente do worker</h2>

  <h3 id="toc-enviornment-scope">Escopo do worker</h3>

  <p>No contexto de um worker, tanto <code>self</code> como <code>this</code> fazem referência ao escopo global d o worker. Portanto, o exemplo anterior poderia ser escrito também assim:</p>

  <pre class="prettyprint">
addEventListener('message', function(e) {
  var data = e.data;
  switch (data.cmd) {
    case 'start':
      postMessage('WORKER STARTED: ' + data.msg);
      break;
    case 'stop':
  ...
}, false);
</pre>

  <p>Uma alternativa seria configurar o manipulador de evento <code>onmessage</code> diretamente (embora<code>addEventListener</code> seja sempre a recomendação dos mestres do JavaScript).</p>

  <pre class="prettyprint">
onmessage = function(e) {
  var data = e.data;
  ...
};
</pre>

  <h3 id="toc-enviornment-features">Recursos disponíveis para workers</h3>

  <p>Devido ao comportamento multi-sequenciado, os web workers só têm acesso a um subconjunto dos recursos do JavaScript:</p>
  <ul>
    <li>O objeto <code>navigator</code></li>
    <li>O objeto <code>location</code> (somente leitura)</li>
    <li><code>XMLHttpRequest</code></li>
    <li><code>setTimeout()/clearTimeout()</code> e <code>setInterval()/clearInterval()</code></li>
    <li>O  <a href="/tutorials/appcache/beginner/">Cache do aplicativo</a> (link em inglês)</li>
    <li>Importação de scripts externos usando o método <code>importScripts()</code></li>
    <li><a href="#toc-enviornment-subworkers">Gerar outros web workers</a></li>
  </ul>
  <p>Os workers NÃO têm acesso a:</p>
  <ul>
    <li>O DOM (não é thread-safe)</li>
    <li>O objeto <code>window</code></li>
    <li>O objeto <code>document</code></li>
    <li>O objeto <code>parent</code></li>
  </ul>

  <h3 id="toc-enviornment-loadingscripts">Como carregar scripts externos</h3>

  <p>É possível carregar arquivos de script ou bibliotecas em um worker com a função <code>importScripts()</code>. O método toma zero ou mais strings que representam os nomes de arquivo para os recursos que serão importados.</p>

  <p>Este exemplo carrega <code>script1.js</code> e <code>script2.js</code> no worker:</p>

  <p>worker.js:</p>
  <pre class="prettyprint">
importScripts('script1.js');
importScripts('script2.js');
</pre>

  <p>O que também pode ser escrito como uma única instrução de importação:</p>
  <pre class="prettyprint">
importScripts('script1.js', 'script2.js');
</pre>

  <h3 id="toc-enviornment-subworkers">Subworkers</h3>

  <p>Os workers têm a habilidade de gerar workers filhos. Isso é ótimo para subdividir tarefas muito grandes no tempo de execução. Contudo, os subworkers vêm com algumas restrições:</p>
  <ul>
    <li>Os subworkers devem ser hospedados na mesma origem que a página pai. </li>
    <li>URIs dentro dos subworkers são resolvidos com relação ao local do worker pai (em oposição à página principal).</li>
  </ul>

  <p>Lembre-se de que a maioria dos navegadores gera processos separados para cada worker. Antes de gerar uma farm de worker, tenha cuidado para não consumir os recursos do sistema do usuário. Um dos motivos para isso é que as mensagens transmitidas entre as páginas principais e os workers são copiadas e não compartilhadas. Consulte <a href="#toc-gettingstarted-workercomm">Como se comunicar com um worker por transmissão de mensagem</a>.</p>

  <p>Para uma amostra de como criar um subworker, veja <a href="http://www.whatwg.org/specs/web-workers/current-work/#delegation">o exemplo</a> (link em inglês) na especificação.</p>

  <h2 id="toc-inlineworkers">Workers embutidos</h2>

  <p>E se você desejasse criar o script do worker durante a execução ou criar uma página independente sem ter de criar arquivos separados de worker? Com a nova interface <a href="://dev.w3.org/2009/dap/file-system/file-writer.html#the-blobbuilder-interface"><code>BlobBuilder</code></a> (link em inglês), é possível "embutir" o worker no mesmo arquivo HTML que a lógica principal pela criação de um <code>BlobBuilder</code> e anexação do código do worker como uma string:</p>
<pre class="prettyprint">
// Prefixed in Webkit, Chrome 12, and FF6: window.WebKitBlobBuilder, window.MozBlobBuilder
var bb = new BlobBuilder();
bb.append("onmessage = function(e) { postMessage('msg from worker'); }");

// Obtain a blob URL reference to our worker 'file'.
// Note: window.webkitURL.createObjectURL() in Chrome 10+.
var blobURL = window.URL.createObjectURL(bb.getBlob());

var worker = new Worker(blobURL);
worker.onmessage = function(e) {
  // e.data == 'msg from worker'
};
worker.postMessage(); // Start the worker.
</pre>

  <h3 id="toc-inlineworkers-bloburis">URLs do Blob</h3>

  <p>A mágica acontece com a chamada para <a href="http://dev.w3.org/2006/webapi/FileAPI/#dfn-createObjectURL"><code>window.URL.createObjectURL()</code></a> (link em inglês). Esse método cria uma string simples de URL, que pode ser usada para fazer referência aos dados armazenados em um <code>File</code> DOM ou objeto <code>Blob</code>. Por exemplo:</p>

  <pre class="prettyprint">blob:http://localhost/c745ef73-ece9-46da-8f66-ebes574789b1</pre>

  <p>Os URLs do Blob são únicos e duram toda a vida do aplicativo (por exemplo, até que o <code>document</code> seja descarregado). Se você for criar muitos URLs do Blob, é recomendável dispensar as referências que não são mais necessárias. É possível dispensar um URL do Blob explicitamente transmitindo-o para <a href="http://dev.w3.org/2006/webapi/FileAPI/#dfn-revokeObjectURL"><code>window.URL.revokeObjectURL()</code></a> (link em inglês):</p>

  <pre class="prettyprint">
window.URL.revokeObjectURL(blobURL); // window.webkitURL.createObjectURL() in Chrome 10+.
</pre>

  <p>No Google Chrome, há uma página boa para visualizar todos os URLs de blob criados: <code>chrome://blob-internals/</code> (link em inglês).</p>

  <h3 id="toc-inlineworkers-example">Exemplo completo</h3>

  <p>Ao dar esse passo, podemos adquirir habilidade na forma como o código JS do worker é embutido na nossa página. Esta técnica usa uma tag <code>&lt;script&gt;</code> para definir o worker:</p>

  <pre class="prettyprint">
&lt;!DOCTYPE html>
&lt;html>
&lt;head>
  &lt;meta charset="utf-8" />
&lt;/head>
&lt;body>

  &lt;div id="log"&gt;&lt;/div&gt;

  &lt;script id="worker1" type="javascript/worker"&gt;
    // This script won't be parsed by JS engines because its type is javascript/worker.
    self.onmessage = function(e) {
      self.postMessage('msg from worker');
    };
    // Rest of your worker code goes here.
  &lt;/script&gt;

  &lt;script&gt;
    function log(msg) {
      // Use a fragment: browser will only render/reflow once.
      var fragment = document.createDocumentFragment();
      fragment.appendChild(document.createTextNode(msg));
      fragment.appendChild(document.createElement('br'));

      document.querySelector("#log").appendChild(fragment);
    }

    var bb = new BlobBuilder();
    bb.append(document.querySelector('#worker1').textContent);

    // Note: window.webkitURL.createObjectURL() in Chrome 10+.
    var worker = new Worker(window.URL.createObjectURL(bb.getBlob()));
    worker.onmessage = function(e) {
      log("Received: " + e.data);
    }
    worker.postMessage(); // Start the worker.
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>

  <p>Em minha opinião, essa nova abordagem é um pouco mais limpa e mais legível. Ela define uma tag de script com <var>id="worker1"</var> e
  <var>type='javascript/worker'</var> (para que o navegador não analise o JS). Esse código é extraído como uma string usando <code>document.querySelector('#worker1').textContent</code> e transmitido para <code>BlobBuilder.append()</code>.</p>

  <h3 id="toc-inlineworkers-loadingscripts">Como carregar scripts externos</h3>

  <p>Ao usar essas técnicas para embutir seu código de worker, o <code>importScripts()</code> funciona apenas se você fornecer um URI absoluto. Se tentar passar um URI relativo, o navegador emite um erro de segurança. A razão disso: o worker (agora criado em um URL de blob) será resolvido com o prefixo <code>blob:</code>, enquanto o aplicativo será executado em um esquema diferente (provavelmente <code>http://</code>). Dessa forma, a falha será devida às restrições de origens diferentes.</p>

  <p>Uma forma de utilizar o <code>importScripts()</code> em um worker embutido é "injetar" o url atual em que script principal é executado pela transmissão dele para o worker embutido e a construção do URL absoluto manualmente. Isso garante que o script externo seja importado da mesma origem. Vamos supor que o aplicativo principal seja executado em  http://example.com/index.html:</p>
    <pre class="prettyprint">
...
&lt;script id="worker2" type="javascript/worker"&gt;
self.onmessage = function(e) {
  var data = e.data;

  if (data.url) {
    var url = data.url.href;
    var index = url.indexOf('index.html');
    if (index != -1) {
      url = url.substring(0, index);
    }
    importScripts(url + 'engine.js');
  }
  ...
};
&lt;/script&gt;
&lt;script&gt;
  var worker = new Worker(window.URL.createObjectURL(bb.getBlob()));
  worker.postMessage(<b>{url: document.location}</b>);
&lt;/script&gt;
</pre>

  <h2 id="toc-errors">Como resolver erros</h2>

  <p>Como toda lógica do JavaScript, é recomendável resolver quaisquer erros que sejam lançados nos web workers. Se ocorrer algum enquanto um worker estiver em execução, um <code>ErrorEvent</code> é acionado. A interface contém três propriedades úteis para descobrir o que deu errado: <code>filename</code> - o nome do script do worker que causou o erro, <code>lineno</code> - o número da linha em que o erro ocorreu e <code>message</code> - uma descrição significativa do erro. Aqui temos um exemplo de configuração de um manipulador de evento <code>onerror</code> para imprimir as propriedades do erro:</p>

  <pre class="prettyprint">
&lt;output id="error" style="color: red;"&gt;&lt;/output&gt;
&lt;output id="result"&gt;&lt;/output&gt;

&lt;script&gt;
  function onError(e) {
    document.getElementById('error').textContent = [
      'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message].join('');
  }

  function onMsg(e) {
    document.getElementById('result').textContent = e.data;
  }

  var worker = new Worker('workerWithError.js');
  worker.addEventListener('message', onMsg, false);
  worker.addEventListener('error', onError, false);
  worker.postMessage(); // Start worker without a message.
&lt;/script&gt;
</pre>

  <p><strong>Exemplo</strong>: workerWithError.js tenta executar 1/x, em que x está indefinido.</p>

  <div class="example">
    <button onclick="startErrorWorker()">Executar</button>
    <output id="error" style="color:red;"></output>
    <output id="result2"></output>
  </div>

  <p>workerWithError.js:</p>
<pre class="prettyprint">
self.addEventListener('message', function(e) {
  postMessage(1/x); // Intentional error.
};
</pre>

  <h2 id="toc-security">Nota sobre segurança</h2>

  <h3 id="toc-security-local">Restrições ao acesso local</h3>

  <p>Devido às restrições de segurança do Google Chrome, os workers não são executados localmente (por exemplo, em <code>file://</code>) nas versões mais recentes do navegador. Em vez disso, eles geram falha silenciosa. Para executar o aplicativo no esquema <code>file://</code>, execute o Google Chrome com o sinal <code>--allow-file-access-from-files</code> definido. <strong>OBSERVAÇÃO</strong>: Não é recomendável executar o navegador principal com esse sinal definido. Ele deve ser usado apenas para fins de teste e não durante a navegação normal.</p>

  <p>Outros navegadores não impõem a mesma restrição.</p>

  <h3 id="toc-security-origin">Algumas considerações sobre a origem</h3>

  <p>Os scripts de workers devem ser arquivos externos com o mesmo esquema de sua página de chamada. Portanto, não é possível carregar um script de um URL <code>data:</code> ou <code>javascript:</code>, e uma página <code>https:</code> não pode iniciar os scripts de worker que começam com URLs <code>http:</code>.</p>

  <h2 id="toc-usecases">Casos de uso</h2>

  <p>Então, qual tipo de aplicativo utilizar com os web workers? Infelizmente, os web workers ainda são relativamente novos e a maioria das amostras/tutoriais que existem envolvem computação de números primos. Ainda que isso não seja muito interessante, é útil para a compreensão dos conceitos de web workers. Seguem mais algumas ideias para você quebrar a cabeça:</p>
  <ul>
    <li>Pré-busca e/ou armazenamento em cache dos dados para usar mais tarde</li>
    <li>Destaque da sintaxe do código ou outra formatação de texto em tempo real</li>
    <li>Corretor ortográfico</li>
    <li>Análise de dados de áudio e vídeo</li>
    <li>E/S em segundo plano ou sondagem de serviços da web</li>
    <li>Processamento de grandes matrizes ou respostas JSON gigantescas</li>
    <li>Filtro de imagens em &lt;canvas&gt;</li>
    <li>Atualização de muitas linhas de um banco de dados Web local</li>
  </ul>

  <h2 id="toc-examples">Demonstrações</h2>
  <ul>
    <li>Exemplo de <a href="http://slides.html5rocks.com/#web-workers">slides do HTML5Rocks</a> (link em inglês)</li>
    <li><a href="http://htmlfive.appspot.com/static/tracker1.html">Rastreador de movimento</a> (link em inglês)</li>
    <li><a href="http://people.mozilla.com/~prouget/demos/worker_and_simulatedannealing/index.xhtml">Otimização simulada</a> (link em inglês)</li>
    <li><a href="http://html5demos.com/worker">Amostras de demonstrações em HTML5</a> (link em inglês)</li>
  </ul>

  <h2 id="toc-references">Referências</h2>
  <ul>
    <li>Especificação <a href="http://www.whatwg.org/specs/web-workers/current-work/">Web Workers</a> (link em inglês)</li>
    <li><a href="http://developer.mozilla.org/en/Using_web_workers">"Using web workers"</a> ("Como usar os web workers", link em inglês) da Rede de Desenvolvedores do Mozilla.</li>
    <li><a href="http://dev.opera.com/articles/view/web-workers-rise-up/">"Web Workers rise up!" ("Web Workers, uni-vos", link em inglês)</a> na Dev.Opera</li>
  </ul>

<script>
  var worker = new Worker('doWork2.js');
  worker.addEventListener('message', function(e) {
    document.getElementById('result').textContent = e.data;
  }, false);

  function sayHI() {
    worker.postMessage({'cmd': 'start', 'msg': 'Hi'});
  }

  function stop() {
    worker.postMessage({'cmd': 'stop', 'msg': 'Bye'});
  }

  function unknownCmd() {
    worker.postMessage({'cmd': 'foobard', 'msg': '???'});
  }

  var worker2 = new Worker('workerWithError.js');
  worker2.addEventListener('message', onMsg, false);
  worker2.addEventListener('error', onError, false);

  function startErrorWorker() {
    worker2.postMessage(); // Start worker without a message.
  }

  function onError(e) {
    document.getElementById('error').textContent = [
      'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message].join('');
  }

  function onMsg(e) {
    document.getElementById('result2').textContent = e.data;
  }
</script>
{% endblock %}